package org.hbgl.geoentity.admin.web.unit;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.hbgl.geoentity.admin.annotation.LoginLog;
import org.hbgl.geoentity.admin.annotation.OperationLog;
import org.hbgl.geoentity.admin.annotation.RequiresPermissionsDesc;
import org.hbgl.geoentity.admin.aspect.OperationType;
import org.hbgl.geoentity.admin.shiro.CustomLoginToken;
import org.hbgl.geoentity.admin.shiro.UserType;
import org.hbgl.geoentity.admin.web.BaseController;
import org.hbgl.geoentity.core.config.RRException;
import org.hbgl.geoentity.core.util.GreetingMaker;
import org.hbgl.geoentity.core.util.IpUtil;
import org.hbgl.geoentity.core.util.JacksonUtil;
import org.hbgl.geoentity.core.util.ResponseUtil;
import org.hbgl.geoentity.core.util.bcrypt.BCryptPasswordEncoder;
import org.hbgl.geoentity.db.domain.GeNode;
import org.hbgl.geoentity.db.domain.GeUnit;
import org.hbgl.geoentity.db.domain.GeUser;
import org.hbgl.geoentity.db.util.CompareValueEnum;
import org.hbgl.geoentity.db.vo.NodeVo;
import org.hbgl.geoentity.db.vo.UnitVo;
import org.hbgl.geoentity.db.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.hbgl.geoentity.admin.util.ReflectUtil;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static org.hbgl.geoentity.admin.util.ResponseCode.*;

@RestController
@RequestMapping("/unit/auth")
@Validated
public class UnitAuthController extends BaseController {

    /**
     * 登录
     *
     * @param body { username : 用户名, password : 密码 }
     */
    @LoginLog(detail = "登录", userType = UserType.UNIT)
    @PostMapping("/login")
    public Object login(@RequestBody String body) {
        String username = JacksonUtil.parseString(body, "username");
        String password = JacksonUtil.parseString(body, "password");
        String code = JacksonUtil.parseString(body, "code");
        if (!ObjectUtils.allNotNull(
                username, password, code
        )) {
            return ResponseUtil.badArgument();
        }
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            return ResponseUtil.badArgumentValue();
        }
        if (StringUtils.isBlank(code)) {
            return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA_REQUIRED, "验证码不能空");
        }

        HttpSession session = request.getSession();
        String kaptcha = (String) session.getAttribute("kaptcha");
        if (!code.equalsIgnoreCase(kaptcha)) {
            return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA, "验证码不正确", doKaptcha());
        }

        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(new CustomLoginToken(username, password, UserType.UNIT));
        } catch (UnknownAccountException uae) {
            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号或密码不正确", doKaptcha());
        } catch (LockedAccountException lae) {
            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号已禁用", doKaptcha());
        } catch (RRException rre) {
            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, rre.getMessage(), doKaptcha());
        } catch (AuthenticationException ae) {
            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "认证失败", doKaptcha());
        }

        GeUser userCurrent = getCurrentUnitUser();

        GeUser user = new GeUser();
        user.setId(userCurrent.getId());
        user.setLastLoginIp(IpUtil.getIpAddr(request));
        user.setLastLoginTime(LocalDateTime.now());
        if (userService.updateByIdSelective(user) == 0) {
            return ResponseUtil.updatedDataFailed();
        }

        UnitVo unit = unitService.selectUnitById(userCurrent.getUnitId());

        // userInfo
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("username", userCurrent.getUsername());

        Map<Object, Object> result = new HashMap<>();
        result.put("token", subject.getSession().getId());
        result.put("userInfo", userInfo);
        result.put("unitInfo", unit);
        return ResponseUtil.ok(result);
    }

    /**
     * 退出
     */
    @PostMapping("/logout")
    public Object logout() {
        Subject subject = SecurityUtils.getSubject();

        subject.logout();
        return ResponseUtil.ok();
    }

    /**
     * 获取当前账号信息
     */
    @GetMapping("/info")
    public Object info() {
        GeUser userCurrent = getCurrentUnitUser();

        UserVo user = userService.selectUnitById(userCurrent.getUnitId(), userCurrent.getId());
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("username", user.getUsername());

        UnitVo unit = unitService.selectUnitTempById(userCurrent.getUnitId());
        if(unit == null) {
            unit = unitService.selectUnitById(userCurrent.getUnitId());
        }

        Map<Object, Object> result = new HashMap<>();
        result.put("userInfo", userInfo);
        result.put("unitInfo", unit);
        return ResponseUtil.ok(result);
    }

    /**
     * 编辑当前账号信息
     *
     * @param unit 单位信息
     */
    @OperationLog(detail = "编辑当前账号信息", operationType = OperationType.UPDATE, userType = UserType.UNIT)
    @PostMapping("/update")
    public Object update(@RequestBody GeUnit unit) {
        GeUser userCurrent = getCurrentUnitUser();

        unit.setId(userCurrent.getUnitId());
        // unit.setUnitIdcode(null);
        // unit.setProvinceCode(null);
        // unit.setCityCode(null);
        // unit.setAreaCode(null);

        //判断是否修改
        GeUnit byId = unitService.findById(userCurrent.getUnitId());
        List<String> exclude = new ArrayList<>();
        exclude.add("id");
        exclude.add("state");
        exclude.add("addTime");
        exclude.add("editTime");
        exclude.add("examState");
        exclude.add("remark");
        Boolean aBoolean = checkData(unit, byId, exclude);
        System.out.println("两个对象值是否相等：" + (aBoolean? "ture": "false"));
        if(!aBoolean) {
            List<GeUnit> unitList = unitService.findByUnitName(unit.getId(), unit.getUnitName());
            if (unitList.size() > 0) {
                return ResponseUtil.fail(USER_NAME_EXIST, "单位名称已经存在");
            }
            Object o = verifyUnitinfoAuthenticity(unit);
            if(o!=null) {
                Map<String, Object> obj = (Map<String, Object>) o;
                if(obj.containsKey("errno")) {
                    if(!obj.get("errno").toString().equals("0")) {
                        return o;
                    }
                }else {
                    return ResponseUtil.fail();
                }
            }

            if (unitService.updateByIdSelective(unit) == 0) {
                return ResponseUtil.updatedDataFailed();
            }
        }
        return ResponseUtil.ok();
    }

    /**
     * 修改当前账号密码
     *
     * @param body { oldPassword : 原密码, newPassword : 新密码 }
     */
    @OperationLog(detail = "修改当前账号密码", operationType = OperationType.UPDATE, userType = UserType.UNIT)
    @PostMapping("/password")
    public Object password(@RequestBody String body) {
        String oldPassword = JacksonUtil.parseString(body, "oldPassword");
        String newPassword = JacksonUtil.parseString(body, "newPassword");
        if (!ObjectUtils.allNotNull(
                oldPassword, newPassword
        )) {
            return ResponseUtil.badArgument();
        }
        if (newPassword.length() < 6) {
            return ResponseUtil.fail(ADMIN_INVALID_PASSWORD, "密码长度不能小于6");
        }

        GeUser userCurrent = getCurrentUnitUser();

        GeUser userLatest = userService.findPasswordById(userCurrent.getId());

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (!encoder.matches(oldPassword, userLatest.getPassword())) {
            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "原密码输入错误");
        }

        GeUser user = new GeUser();
        user.setId(userCurrent.getId());
        user.setPassword(encoder.encode(newPassword));

        if (userService.updateByIdSelective(user) == 0) {
            return ResponseUtil.updatedDataFailed();
        }

        return ResponseUtil.ok();
    }

    /**
     * 首页信息
     */
    @GetMapping("/homeInfo")
    public Object homeInfo() {
        GeUser userCurrent = getCurrentUnitUser();
        UnitVo user = unitService.selectUnitTempById(userCurrent.getUnitId());
        if(user == null) {
            user = unitService.selectUnitById(userCurrent.getUnitId());
        }

        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("username", user.getUnitName());
        userInfo.put("examState", user.getExamState());
        // userInfo.put("realname", user.getRealname());
        // userInfo.put("sex", user.getSex());
        // userInfo.put("telephone", user.getTelephone());
        // userInfo.put("email", user.getEmail());

        GeUnit geUnit = new GeUnit();
        geUnit.setLinkman(user.getLinkman());
        geUnit.setLinkphone(user.getLinkphone());
        geUnit.setUnitName(user.getUnitName());
        geUnit.setRemark(user.getRemark());
        userInfo.put("unit", geUnit);
        String greeting = GreetingMaker.printTimeOfDay();
        userInfo.put("greeting", greeting);
        //通过用户id获取节点信息
        List<NodeVo> nodeVos = nodeService.selectNodeListNew(-1, -2, null, 1, 10);
        GeNode geNode = new GeNode();
        if(nodeVos != null && nodeVos.size()>0) {
            nodeVos = nodeVos.stream().filter(p->p.getUnitId().equals(userCurrent.getUnitId())).collect(Collectors.toList());
            if(nodeVos != null && nodeVos.size()>0) {
                NodeVo nodeVo = nodeVos.get(0);
                geNode.setNodeIdcode(nodeVo.getNodeIdcode());
                geNode.setNodeName(nodeVo.getNodeName());
                geNode.setNodeType(nodeVo.getNodeType());

            }
        }
        userInfo.put("node", geNode);
        Map<Object, Object> result = new HashMap<>();
        result.put("userInfo", userInfo);
        return ResponseUtil.ok(result);
    }

    /**
     * 判断属性值是否发生变化
     * @param class1 新数据
     * @param class2 历史数据
     * @param filterFields 不需要比对的字段
     * @return boolean 有变化返回true
     */
    private static <T> Boolean checkData(T class1, T class2, List<String> filterFields) {
        //判断是否为同类
        if(class1.getClass() != class2.getClass()){
            throw new ClassCastException("数据类型不一致");
        }
        //获取所有类字段
        List<Field> fields = ReflectUtil.listFields(class1.getClass());
//        过滤不需要比对的字段
        List<Field> fieldList = fields.stream().filter(f->!filterFields.contains(f.getName())).collect(Collectors.toList());
        for (Field field : fieldList) {
            field.setAccessible(true);
            try {
                //获取属性值
                Object value1 = field.get(class1);
                System.out.println("value1=" + value1.toString());
                Object value2 = field.get(class2);
                System.out.println("value2=" + value2.toString());
                //class1属性值不为空，class2属性值为空
                if (!org.springframework.util.ObjectUtils.isEmpty(value1) && org.springframework.util.ObjectUtils.isEmpty(value2)){
                    return false;
                }
                //class1属性值为空，class2属性值不为空
                if (org.springframework.util.ObjectUtils.isEmpty(value1) && !org.springframework.util.ObjectUtils.isEmpty(value2)){
                    return false;
                }
                //值不为空，属性值不等
                if (!org.springframework.util.ObjectUtils.isEmpty(value1) && !org.springframework.util.ObjectUtils.isEmpty(value2)){
                    //对比属性值是否相同
//                    if(CompareValueEnum.FiledType(value1,value2)){
                    if(!value1.equals(value2)){
                        return false;
                    }
                }

            } catch (Exception e) {
            }
        }
        return true;
    }



}
